Skip to content

Long range query support #3299

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Sep 6, 2018
Merged

Long range query support #3299

merged 10 commits into from
Sep 6, 2018

Conversation

annashlyak
Copy link
Contributor

Hello, everybody,

I want to make range queries that correctly work with long type. So, I have added support of long range query.

Small test to demonstrate trouble that I want to solve .
We want to work with timestamps that are long type:

q.Range(c => c
	.Name("named_query")
	.Boost(1.1)
	.Field(p => p.Description)
	.GreaterThan(636634079999999999)
	.GreaterThanOrEquals(636634080000000000)
	.LessThan(636634080000000000)
	.LessThanOrEquals(636634079999999999)
	.Relation(RangeRelation.Within))

converts numbers to double type:

   query = new {
          range = new {
            description = new {
              _name = "named_query",
              boost = 1.1,
              gt = 6.3663408E+17,
              gte = 6.3663408E+17,
              lt = 6.3663408E+17,
              lte = 6.3663408E+17,
              relation = "within"
            }
          }
        }

and loses precision.
Elasticsearch himself is able to make such requests, but in NEST this functionality is missed.

I will be grateful if you merge my pull request and I'm ready to discuss my decision.

@karmi
Copy link

karmi commented Jun 20, 2018

Hi @annashlyak, we have found your signature in our records, but it seems like you have signed with a different e-mail than the one used in yout Git commit. Can you please add both of these e-mails into your Github profile (they can be hidden), so we can match your e-mails to your Github profile?

@annashlyak
Copy link
Contributor Author

Hi @karmi, I have added email to my profile

@Mpdreamz
Copy link
Member

Thanks for the PR @annashlyak 😄 this LGTM.

Copy link
Contributor

@russcam russcam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR @annashlyak! I've left a couple of small comments, when you have a chance to take a look


var isNumeric = !jo.Properties().Any(p => p.Name == "format" || p.Name == "time_zone")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible to refactor this to remove the duplicated code within isLong and isNumeric checks. It may be possible to reduce down to one enumeration/iteration of jo.Properties to determine IRangeQuery

@@ -79,7 +79,9 @@ private void Write(string queryType, Field field = null)

public virtual void Visit(INumericRangeQuery query) => Write("numeric_range");

public virtual void Visit(ITermRangeQuery query) => Write("term_range");
public virtual void Visit(ILongRangeQuery query) => Write("numeric_range");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

long_range?

@russcam
Copy link
Contributor

russcam commented Aug 22, 2018

would you like me to take another look at the PR @annashlyak - is it ready to review again?

@annashlyak
Copy link
Contributor Author

@russcam yes, you can watch review again

Copy link
Contributor

@russcam russcam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @annashlyak, I've left a comment to simplify determining the range query.

Let me know what you think 🙂

var nameIsValid = !jo.Properties().Any(p => p.Name == "format" || p.Name == "time_zone");

var isLong = nameIsValid && CheckType(jo, JTokenType.Integer);
var isNumeric = nameIsValid && CheckType(jo, JTokenType.Float);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be simplified further.

For isNumeric and DateRangeQuery cases, CheckType will end up being called twice. It looks like it should be possible to enumerate/iterate over jo.Properties() once and determine from the existence of "format", "time_zone" or token type, which IRangeQuery it is. I was thinking something like

private static IRangeQuery GetRangeQuery(JsonSerializer serializer, JObject jo)
{
	foreach (var property in jo.Properties())
	{
		if (property.Name == "format" || property.Name == "time_zone")
			return FromJson.ReadAs<DateRangeQuery>(jo.CreateReader(), serializer);

		if (_rangeKeys.Contains(property.Name))
		{
			if (property.Value.Type == JTokenType.Integer)
				return FromJson.ReadAs<LongRangeQuery>(jo.CreateReader(), serializer);					
			if (property.Value.Type == JTokenType.Float)
				return FromJson.ReadAs<NumericRangeQuery>(jo.CreateReader(), serializer);
			
			return FromJson.ReadAs<DateRangeQuery>(jo.CreateReader(), serializer);
		}
	}
}

what do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, thank you. LGTM. "format" and "time_zone" checks must be done for all properties in jo. When we check for a type, we can not immediately return the result, because LongRange is used, if there was no property of type float, but there was a property of type int. However, we can iterate once and I think that the code will looks better.

Copy link
Contributor

@russcam russcam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some additional comments @annashlyak. Let me know your thoughts

@@ -37,36 +37,30 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist

private static IRangeQuery GetRangeQuery(JsonSerializer serializer, JObject jo)
{
var nameIsValid = !jo.Properties().Any(p => p.Name == "format" || p.Name == "time_zone");
IRangeQuery fq = FromJson.ReadAs<DateRangeQuery>(jo.CreateReader(), serializer);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this point, we don't know if the range query is one on date fields. I think assigning null is more appropriate

IRangeQuery fq = null;

and deserializing to DateRangeQuery only when it is known that the query is one on date fields.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Earlier we deserialize to DateRangeQuery always when check for numeric Type was false. I think that we can just not declare fq.

if (_rangeKeys.Contains(property.Name))
{
if (property.Value.Type == JTokenType.Float)
isNumeric = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If any one of the range properties is a float JSON type, then I think it's safe to return NumericRangeQuery query here.

The assumption that can't be made, if I've understood correctly, is that we can't rule out NumericRangeQuery if a range property is an integer JSON type until we've seen all the range properties i.e. put another way, we can't be sure to return LongRangeQuery until we've seen all of the range properties, and observed that they are all integer JSON types.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Earlier was check !jo.Properties().Any(p => p.Name == "format" || p.Name == "time_zone") It is the same as jo.Properties().All(p => p.Name != "format" && p.Name != "time_zone"). So, we have to done this for all properties and I dont want return here NumericRangeQuery

@Mpdreamz Mpdreamz mentioned this pull request Sep 3, 2018
45 tasks
@russcam
Copy link
Contributor

russcam commented Sep 6, 2018

This looks great, @annashlyak! Thank you for your effort 👍

@russcam russcam merged commit 69733ea into elastic:6.x Sep 6, 2018
Mpdreamz pushed a commit that referenced this pull request Sep 10, 2018
add LongRangeQuery to handle range queries on long numeric field types.

(cherry picked from commit 69733ea)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants